home *** CD-ROM | disk | FTP | other *** search
/ The Utilities Experience / The Utilities Experience - Volume 1.iso / software / emulation / frodo / src / 1541fs.c < prev    next >
C/C++ Source or Header  |  1996-01-29  |  12KB  |  548 lines

  1. /*
  2.  *  1541fs.c - 1541-Emulation im Amiga-Dateisystem
  3.  *
  4.  *  Copyright (C) 1994-1996 by Christian Bauer
  5.  */
  6.  
  7. /*
  8.  *  Anmerkungen:
  9.  *  ------------
  10.  *
  11.  *  Routinen:
  12.  *   - Die Schnittstelle zu den IEC-Routinen besteht in den Routinen
  13.  *     FS_Init, FS_Exit, FS_Open, FS_Close, FS_Read und FS_Write:
  14.  *       FS_Init bereitet die Emulation vor
  15.  *       FS_Exit beendet die Emulation
  16.  *       FS_Open öffnet einen Kanal
  17.  *       FS_Close schließt einen Kanal
  18.  *       FS_Read liest aus einem Kanal
  19.  *       FS_Write schreibt in einen Kanal
  20.  *
  21.  *  DriveData:
  22.  *   - lock enthält den Lock des Verzeichnisses, in dem die Emulation
  23.  *     ablaufen soll
  24.  *
  25.  *  Directory-Emulation:
  26.  *   - Wird das Directory geöffnet (Dateiname "$"), wird in T: eine
  27.  *     temporäre Datei angelegt, die vom Aufbau genau einem 1541-Directory
  28.  *     entspricht und diese Datei geöffnet. Sie kann dann mit den ganz
  29.  *     normalen Lesebefehlen verarbeitet werden.
  30.  *
  31.  *  Inkompatibilitäten/Verbesserungen:
  32.  *   - Keine Wildcards beim Directory-Laden
  33.  *   - Kein "rohes" Directory-Lesen
  34.  *   - Keine relativen Dateien
  35.  *   - Nur 'I'- und 'UJ'-Befehle implementiert
  36.  */
  37.  
  38. #include <exec/types.h>
  39. #include <exec/memory.h>
  40. #include <clib/exec_protos.h>
  41. #include <clib/dos_protos.h>
  42. #include <string.h>
  43.  
  44. #include "IEC.h"
  45. #include "1541fs.h"
  46. #include "Display.h"
  47. #define CATCOMP_NUMBERS 1
  48. #include "LocStrings.h"
  49.  
  50.  
  51. // Aus Main.asm
  52. extern void ResetC64(void);
  53.  
  54.  
  55. // Prototypes
  56. int open_file(DriveData *drive, int channel, const char *filename);
  57. void convert_filename(const char *filename, char *plainname, int *filemode, int *filetype, int *wildflag);
  58. void find_first_file(char *name);
  59. int open_directory(DriveData *drive, int channel, const char *filename);
  60. void close_all_channels(DriveData *drive);
  61. void execute_command(DriveData *drive, const char *command);
  62. char conv_from_64(char c);
  63. char conv_to_64(char c);
  64.  
  65.  
  66. /**
  67.  **  Emulation vorbereiten, Lock auf Verzeichnis holen, prefs zeigt auf den
  68.  **    Preferences-String
  69.  **/
  70.  
  71. void FS_Init(DriveData *drive, char *prefs)
  72. {
  73.   int i;
  74.  
  75.   if (drive->lock = Lock(prefs,ACCESS_READ)) {
  76.     for (i=0; i<16; i++) drive->handle[i] = NULL;
  77.  
  78.     drive->cmd_length = 0;
  79.  
  80.     SetError(drive, ERR_STARTUP);
  81.   }
  82. }
  83.  
  84.  
  85. /**
  86.  **  Emulation beenden, Lock auf Verzeichnis freigeben
  87.  **/
  88.  
  89. void FS_Exit(DriveData *drive)
  90. {
  91.   if (drive->lock) {
  92.     close_all_channels(drive);
  93.  
  94.     UnLock(drive->lock);
  95.     drive->lock = NULL;
  96.   }
  97. }
  98.  
  99.  
  100. /**
  101.  **  Kanal öffnen, filename ist Null-terminiert
  102.  **/
  103.  
  104. int FS_Open(DriveData *drive, int channel, char *filename)
  105. {
  106.   SetError(drive, ERR_OK);
  107.  
  108.   // Kanal 15: Dateiname als Befehl ausführen
  109.   if (channel == 15) {
  110.     execute_command(drive, filename);
  111.     return ST_OK;
  112.   }
  113.  
  114.   // Vorige Datei schließen, wenn noch offen
  115.   if (drive->handle[channel]) {
  116.     Close(drive->handle[channel]);
  117.     drive->handle[channel] = NULL;
  118.   }
  119.  
  120.   if (filename[0] == '$')
  121.     return open_directory(drive, channel, filename);
  122.  
  123.   if (filename[0] == '#') {
  124.     SetError(drive, ERR_NOCHANNEL);
  125.     return ST_OK;
  126.   }
  127.  
  128.   return open_file(drive, channel, filename);
  129. }
  130.  
  131.  
  132. /*
  133.  *  Datei wird geöffnet
  134.  */
  135.  
  136. // Zugriffsmodi
  137. enum {
  138.   FMODE_READ, FMODE_WRITE, FMODE_APPEND
  139. };
  140.  
  141. // Dateitypen
  142. enum {
  143.   FTYPE_PRG, FTYPE_SEQ
  144. };
  145.  
  146. int open_file(DriveData *drive, int channel, const char *filename)
  147. {
  148.   BPTR olddir;
  149.   BPTR fh = NULL;
  150.   char plainname[256];
  151.   int filemode = FMODE_READ;
  152.   int filetype = FTYPE_PRG;
  153.   int wildflag = 0;
  154.  
  155.   olddir = CurrentDir(drive->lock);
  156.  
  157.   convert_filename(filename, plainname, &filemode, &filetype, &wildflag);
  158.  
  159.   // Bei Kanal 0 immer PRG lesen, bei Kanal 1 immer PRG schreiben
  160.   if (!channel) {
  161.     filemode = FMODE_READ;
  162.     filetype = FTYPE_PRG;
  163.   }
  164.   if (channel == 1) {
  165.     filemode = FMODE_WRITE;
  166.     filetype = FTYPE_PRG;
  167.   }
  168.  
  169.   // Wildcards sind nur beim Lesen erlaubt
  170.   if (wildflag) {
  171.     if (filemode != FMODE_READ) {
  172.       SetError(drive, ERR_SYNTAX33);
  173.       CurrentDir(olddir);
  174.       return ST_OK;
  175.     }
  176.     find_first_file(plainname);
  177.   }
  178.  
  179.   if (fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE)) {
  180.     switch (filemode) {
  181.  
  182.       // Erstes Zeichen lesen, wenn zum Lesen geöffnet
  183.       case FMODE_READ:
  184.         drive->read_char = FGetC(fh);
  185.         break;
  186.  
  187.       // Neue Datei anlegen: E-Bit bei sequentieller Datei löschen
  188.       case FMODE_WRITE:
  189.         if (filetype == FTYPE_SEQ) {
  190.           Close(fh);
  191.           SetProtection(plainname, FIBF_EXECUTE);
  192.           fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE);
  193.         } else {
  194.           Close(fh);
  195.           SetProtection(plainname, 0);
  196.           fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE);
  197.         }
  198.         break;
  199.  
  200.       // Anhängen: Ans Ende der Datei gehen
  201.       case FMODE_APPEND:
  202.         Seek(fh, 0, OFFSET_END);
  203.         break;
  204.     }
  205.   }
  206.  
  207.   if (!(drive->handle[channel] = fh))
  208.     SetError(drive, ERR_FILENOTFOUND);
  209.  
  210.   CurrentDir(olddir);
  211.   return ST_OK;
  212. }
  213.  
  214.  
  215. /*
  216.  *  Dateibezeichnung analysieren, Dateimodus und -typ ermitteln
  217.  */
  218.  
  219. void convert_filename(const char *srcname, char *destname, int *filemode, int *filetype, int *wildflag)
  220. {
  221.   char *p, *q;
  222.   int i;
  223.  
  224.   // Nach ':' suchen, p zeigt auf erstes Zeichen hinter dem ':'
  225.   if (p = strchr(srcname, ':'))
  226.     p++;
  227.   else
  228.     p = srcname;
  229.  
  230.   // Zeichensatz des Reststrings wandeln -> destname
  231.   q = destname;
  232.   for (i=0; i<NAMEBUF_LENGTH && (*q++ = conv_from_64(*p++)); i++);
  233.  
  234.   // Nach Modusparametern, getrennt durch ',' suchen
  235.   p = destname;
  236.   while (p = strchr(p, ',')) {
  237.  
  238.     // String hinter dem ersten ',' abschneiden
  239.     *p++ = 0;
  240.  
  241.     switch (*p) {
  242.       case 'p':
  243.         *filetype = FTYPE_PRG;
  244.         break;
  245.       case 's':
  246.         *filetype = FTYPE_SEQ;
  247.         break;
  248.       case 'r':
  249.         *filemode = FMODE_READ;
  250.         break;
  251.       case 'w':
  252.         *filemode = FMODE_WRITE;
  253.         break;
  254.       case 'a':
  255.         *filemode = FMODE_APPEND;
  256.         break;
  257.     }
  258.   }
  259.  
  260.   // Nach Wildcards suchen, '*' durch '#?' ersetzen und alles danach abschneiden
  261.   *wildflag = (strchr(destname, '?') != NULL);
  262.  
  263.   if (p = strchr(destname, '*')) {
  264.     *p++ = '#';
  265.     *p++ = '?';
  266.     *p++ = 0;
  267.     *wildflag = TRUE;
  268.   }
  269. }
  270.  
  271.  
  272. /*
  273.  *  Erste zum Muster passende Datei suchen den Dateinamen ermitteln
  274.  */
  275.  
  276. void find_first_file(char *name)
  277. {
  278.   struct AnchorPath *a;
  279.  
  280.   if (a = AllocMem(sizeof(struct AnchorPath), MEMF_CLEAR|MEMF_PUBLIC)) {
  281.     if (!MatchFirst(name, a)) {
  282.       strncpy(name, a->ap_Info.fib_FileName, NAMEBUF_LENGTH);
  283.     }
  284.     MatchEnd(a);
  285.     FreeMem(a, sizeof(struct AnchorPath));
  286.   }
  287. }
  288.  
  289.  
  290. /*
  291.  *  Directory wird geöffnet, temporäre Datei erstellen
  292.  */
  293.  
  294. struct FileInfoBlock fib;
  295.  
  296. int open_directory(DriveData *drive, int channel, const char *filename)
  297. {
  298.   BPTR fh;
  299.   char buf[] = "\001\004\001\001\0\0\022\042                \042 00 2A";
  300.   char *p, *q;
  301.   int i;
  302.  
  303.   if (!Examine(drive->lock, &fib)) return ST_OK;
  304.  
  305.   if (!(fh = Open("T:Frodo$File", MODE_NEWFILE))) return ST_OK;
  306.  
  307.   // Directory-Titel erzeugen und schreiben
  308.   p = &buf[8];
  309.   for (i=0; i<16 && fib.fib_FileName[i]; i++)
  310.     *p++ = conv_to_64(fib.fib_FileName[i]);
  311.   Write(fh, buf, 32);
  312.  
  313.   // Für jeden Verzeichniseintrag eine Zeile erzeugen und schreiben
  314.   while (ExNext(drive->lock, &fib)) {
  315.  
  316.     // Zeile mit Leerzeichen löschen und mit Nullbyte abschließen
  317.     memset(buf, ' ', 31);
  318.     buf[31] = 0;
  319.  
  320.     p = buf;
  321.     *p++ = 0x01;    // Dummy-Verkettung
  322.     *p++ = 0x01;
  323.  
  324.     // Größe in Blocks berechnen und eintragen
  325.     i = (fib.fib_Size + 254) / 254;
  326.     *p++ = i & 0xff;
  327.     *p++ = (i >> 8) & 0xff;
  328.  
  329.     p++;
  330.     if (i < 10) p++;    // Kleiner als 10: Ein Leerzeichen dazunehmen
  331.     if (i < 100) p++;    // Kleiner als 100: Noch ein Leerzeichen dazunehmen
  332.  
  333.     // Dateiname wandeln und eintragen
  334.     *p++ = '\"';
  335.     q = p;
  336.     for (i=0; i<16 && fib.fib_FileName[i]; i++)
  337.       if (fib.fib_FileName[i])
  338.         *q++ = conv_to_64(fib.fib_FileName[i]);
  339.     *q++ = '\"';
  340.     p += 18;
  341.  
  342.     // Typ ermitteln und eintragen
  343.     if (fib.fib_DirEntryType >= 0) {
  344.       *p++ = 'D';
  345.       *p++ = 'I';
  346.       *p++ = 'R';
  347.     } else if (fib.fib_Protection & FIBF_EXECUTE) {
  348.         *p++ = 'S';
  349.         *p++ = 'E';
  350.         *p++ = 'Q';
  351.       } else {
  352.         *p++ = 'P';
  353.         *p++ = 'R';
  354.         *p++ = 'G';
  355.       }
  356.  
  357.     // Datei geschützt?
  358.     if (fib.fib_Protection & FIBF_DELETE) *p++ = '<';
  359.  
  360.     // Zeile schreiben
  361.     Write(fh, buf, 32);
  362.   }
  363.  
  364.   // Abschlußzeile
  365.   Write(fh, "\001\001\0\0BLOCKS FREE.             \0\0", 32);
  366.   Close(fh);
  367.  
  368.   // Datei zum Lesen öffnen und das erste Zeichen lesen
  369.   if (drive->handle[channel] = Open("T:Frodo$File", MODE_OLDFILE))
  370.     drive->read_char = FGetC(drive->handle[channel]);
  371.  
  372.   return ST_OK;
  373. }
  374.  
  375.  
  376. /**
  377.  **  Kanal schließen
  378.  **/
  379.  
  380. int FS_Close(DriveData *drive, int channel)
  381. {
  382.   if (channel==15) {
  383.     close_all_channels(drive);
  384.     return ST_OK;
  385.   }
  386.  
  387.   if (drive->handle[channel]) {
  388.     Close(drive->handle[channel]);
  389.     drive->handle[channel] = NULL;
  390.   }
  391.  
  392.   return ST_OK;
  393. }
  394.  
  395.  
  396. /*
  397.  *  Alle Kanäle schließen
  398.  */
  399.  
  400. void close_all_channels(DriveData *drive)
  401. {
  402.   int i;
  403.  
  404.   for (i=0; i<15; i++) FS_Close(drive, i);
  405.  
  406.   drive->cmd_length = 0;
  407. }
  408.  
  409.  
  410. /**
  411.  **  Ein Byte aus Kanal lesen
  412.  **/
  413.  
  414. int FS_Read(DriveData *drive, int channel, char *data)
  415. {
  416.   LONG c;
  417.  
  418.   // Kanal 15: Fehlerkanal
  419.   if (channel == 15) {
  420.     *data = *drive->error_ptr++;
  421.  
  422.     if (*data != '\r')
  423.       return ST_OK;
  424.     else {
  425.       SetError(drive, ERR_OK);
  426.       return ST_EOF;
  427.     }
  428.   }
  429.  
  430.   if (!drive->handle[channel]) return ST_READ_TIMEOUT;
  431.  
  432.   // Zeichen aus dem Puffer holen und nächstes Zeichen lesen
  433.   *data = drive->read_char;
  434.   c = FGetC(drive->handle[channel]);
  435.   drive->read_char = c;
  436.  
  437.   if (c == -1)
  438.     return ST_EOF;
  439.   else
  440.     return ST_OK;
  441. }
  442.  
  443.  
  444. /**
  445.  **  Ein Byte in Kanal schreiben
  446.  **/
  447.  
  448. int FS_Write(DriveData *drive, int channel, char data, char eof)
  449. {
  450.   // Kanal 15: Zeichen sammeln und bei EOF Befehl ausführen
  451.   if (channel == 15) {
  452.     if (drive->cmd_length >= 40)
  453.       return ST_TIMEOUT;
  454.  
  455.     drive->cmd_buffer[drive->cmd_length++] = data;
  456.  
  457.     if (eof < 0) {
  458.       drive->cmd_buffer[drive->cmd_length++] = 0;
  459.       drive->cmd_length = 0;
  460.       execute_command(drive, drive->cmd_buffer);
  461.     }
  462.     return ST_OK;
  463.   }
  464.  
  465.   if (!drive->handle[channel]) {
  466.     SetError(drive, ERR_FILENOTOPEN);
  467.     return ST_TIMEOUT;
  468.   }
  469.  
  470.   if (FPutC(drive->handle[channel], (unsigned char)data) < 0) {
  471.     SetError(drive, ERR_WRITEERROR);
  472.     return ST_TIMEOUT;
  473.   }
  474.  
  475.   return ST_OK;
  476. }
  477.  
  478.  
  479. /*
  480.  *  Befehlsstring ausführen
  481.  */
  482.  
  483. void execute_command(DriveData *drive, const char *command)
  484. {
  485.   APTR args;
  486.  
  487.   switch (command[0]) {
  488.     case 'B':
  489.       args = "B-?";
  490.       if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
  491.         ResetC64();
  492.       SetError(drive, ERR_SYNTAX30);
  493.       break;
  494.  
  495.     case 'M':
  496.       args = "M-?";
  497.       if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
  498.         ResetC64();
  499.       SetError(drive, ERR_SYNTAX30);
  500.       break;
  501.  
  502.     case 'I':
  503.       close_all_channels(drive);
  504.       SetError(drive, ERR_OK);
  505.       break;
  506.  
  507.     case 'U':
  508.       if ((command[1] & 0x0f) == 0x0a) {
  509.         close_all_channels(drive);
  510.         SetError(drive, ERR_STARTUP);
  511.       } else
  512.         SetError(drive, ERR_SYNTAX30);
  513.       break;
  514.  
  515.     default:
  516.       SetError(drive, ERR_SYNTAX30);
  517.       break;
  518.   }
  519. }
  520.  
  521.  
  522. /*
  523.  *  Umwandlung PETSCII->ASCII
  524.  */
  525.  
  526. char conv_from_64(char c)
  527. {
  528.   if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
  529.     return c ^ 0x20;
  530.   if ((c == '/') && MapSlash)
  531.     return '\\';
  532.   return c;
  533. }
  534.  
  535.  
  536. /*
  537.  *  Umwandlung ASCII->PETSCII
  538.  */
  539.  
  540. char conv_to_64(char c)
  541. {
  542.   if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
  543.     return c ^ 0x20;
  544.   if ((c == '\\') && MapSlash)
  545.     return '/';
  546.   return c;
  547. }
  548.